/*
 * GD32F4xx I2C emulation
 *
 * Copyright (c) 2022-, Huawei
 * Written by crisis.yangyong
 *
 * This code is licensed under the GNU GPLv2 and later.
 */
#include <string.h>
#include "qemu/osdep.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "sysemu/runstate.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "hw/i2c/i2c.h"
#include "qemu/qemu-print.h"
#include "qapi/visitor.h"
#include "chardev/char-fe.h"

#define TYPE_GD32F4_I2C "gd32f4-i2c"
#define GD32F4_I2C(obj) OBJECT_CHECK(gd32f4_i2c_state, (obj), TYPE_GD32F4_I2C)
#define GD32F4_I2C_SHIFT_3          3
#define GD32F4_I2C_READ_FRAME_LEN   32

enum {
    GD32F4_I2C_REG_CTL0 = 0,    // 0x00 >>2 =0x00
    GD32F4_I2C_REG_CTL1,        // 0x04 >>2 =0x01
    GD32F4_I2C_REG_SADDR0,      // 0x08 >>2 =0x02
    GD32F4_I2C_REG_SADDR1,      // 0x0c >>2 =0x03
    GD32F4_I2C_REG_DATA,        // 0x10 >>2 =0x04
    GD32F4_I2C_REG_STAT0,       // 0x14 >>2 =0x05
    GD32F4_I2C_REG_STAT1,       // 0x18 >>2 =0x06
    GD32F4_I2C_REG_CKCFG,       // 0x1c >>2 =0x07
    GD32F4_I2C_REG_RT,          // 0x20 >>2 =0x08
    GD32F4_I2C_REG_FCTL,        // 0x24 >>2 =0x09
    GD32F4_I2C_REG_SAMCS = 32,  // 0x80 >>2 =0x20
};

typedef struct {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    I2CBus *bus;
    CharBackend chrbe;
    bool transfer_start;
    bool transfer_recv;
    qemu_irq irq[2];    // irq[0]: event, irq[1]: error
    uint32_t reg[0x100];
    uint16_t rx_temp;
    uint16_t tx_temp;
    bool do_stop;
    bool stat0_read;
    bool stat1_read;
    bool ctl0_write;
    bool data_write;
} gd32f4_i2c_state;

static const VMStateDescription vmstate_gd32f4_i2c = {
    .name = TYPE_GD32F4_I2C,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(reg, gd32f4_i2c_state, 0x100),
        VMSTATE_BOOL(do_stop, gd32f4_i2c_state),
        VMSTATE_UINT16(rx_temp, gd32f4_i2c_state),
        VMSTATE_UINT16(tx_temp, gd32f4_i2c_state),
        VMSTATE_END_OF_LIST()
    }
};

static void gd32f4_i2c_reset(DeviceState *d)
{
    gd32f4_i2c_state *s = GD32F4_I2C(d);
    memset(s->reg,  0, sizeof(s->reg));
    s->reg[GD32F4_I2C_REG_RT] = 0x2;
}

static void gd32f4_i2c_read_reg_data(gd32f4_i2c_state *s)
{
    if (s->reg[GD32F4_I2C_REG_STAT0] & 0x40) {  // RBNE set
        if (s->reg[GD32F4_I2C_REG_STAT1] & 0x2) {   // I2CBUSY set, xrecv next byte for next read
            s->rx_temp = i2c_recv(s->bus) & 0xFF;
            if (s->reg[GD32F4_I2C_REG_CTL1] & 0x600) {   // EVIE or BUFIE set
                qemu_irq_pulse(s->irq[0]);
            }
        } else {
            s->reg[GD32F4_I2C_REG_STAT0] &= ~0x40;  // RBNE clear
        }
        if (s->do_stop) {
            s->do_stop = false;
            i2c_end_transfer(s->bus);
            s->reg[GD32F4_I2C_REG_CTL0] &= ~0x200;
            s->reg[GD32F4_I2C_REG_STAT0] &= ~0x87;  // SBSEND & ADDSEND & BTC & TBE clear
            s->reg[GD32F4_I2C_REG_STAT1] &= ~0x7;   // MASTER & I2CBUSY & TR clear
        }
    }
}

static uint64_t gd32f4_i2c_read(void *opaque, hwaddr offset, unsigned size)
{
    gd32f4_i2c_state *s = (gd32f4_i2c_state *)opaque;
    const uint32_t reg_index = (uint32_t)(offset >> 2);
    uint32_t reg_val = s->reg[reg_index];

    if (reg_index == GD32F4_I2C_REG_DATA) {
        reg_val = s->rx_temp & 0xFF;
        gd32f4_i2c_read_reg_data(s);
    } else if (reg_index == GD32F4_I2C_REG_STAT0) {
        s->stat0_read = true;
        if (s->stat1_read) {
            s->reg[GD32F4_I2C_REG_STAT0] &= ~0x2;
            s->stat1_read = false;
        }
    } else if (reg_index == GD32F4_I2C_REG_STAT1) {
        s->stat1_read = true;
        if (s->stat0_read) {
            s->reg[GD32F4_I2C_REG_STAT0] &= ~0x2;
            s->stat1_read = false;
        }
    }
    if (size == sizeof(uint32_t)) {
        return reg_val;
    } else {
        return *(uint16_t *)((uint8_t *)&reg_val + (offset & GD32F4_I2C_SHIFT_3));
    }
}

static void gd32f4_i2c_write_reg_ctl0(gd32f4_i2c_state *s)
{
    if (s->reg[GD32F4_I2C_REG_CTL0] & 0x8000) { // SRESET
        gd32f4_i2c_reset(DEVICE(s));
        return;
    }

    if ((s->reg[GD32F4_I2C_REG_CTL0] & 0x2) && \
         (s->reg[GD32F4_I2C_REG_CTL0] & 0x10) && \
         (s->reg[GD32F4_I2C_REG_CTL0] & 0x1000)) {
        printf("%s: do not support SMBus mode now!\n", TYPE_GD32F4_I2C);
    }
    if (s->reg[GD32F4_I2C_REG_CTL0] & 0x200) {  // STOP
        bool is_read = (s->reg[GD32F4_I2C_REG_STAT0] & 0x40) ? true : false;    // RBNE set if read operation
        if (!is_read) {
            i2c_end_transfer(s->bus);
            s->reg[GD32F4_I2C_REG_CTL0] &= ~0x200;
            s->reg[GD32F4_I2C_REG_STAT0] &= ~0xc7;  // SBSEND & ADDSEND & BTC & RBNE & TBE clear
            s->reg[GD32F4_I2C_REG_STAT1] &= ~0x7;   // MASTER & I2CBUSY & TR clear
        } else {
            s->do_stop = true;
        }
    }
    if (s->reg[GD32F4_I2C_REG_CTL0] & 0x100) {  // START
        s->do_stop = false;
        s->reg[GD32F4_I2C_REG_CTL0] &= ~0x100;
        s->reg[GD32F4_I2C_REG_STAT0] |= 0x1;    // SBSEND set
        s->reg[GD32F4_I2C_REG_STAT1] |= 0x3;    // MASTER & I2CBUSY set
        s->reg[GD32F4_I2C_REG_STAT0] &= ~0xc6;  // ADDRSEND & BTC & RBNE & TBE clear
        s->reg[GD32F4_I2C_REG_STAT1] &= ~0x4;   // TR clear
        if (s->reg[GD32F4_I2C_REG_CTL1] & 0x200) {  // EVIE set
            qemu_irq_pulse(s->irq[0]);
            qemu_printf("GD32F4_I2C_REG_CTL0 START write trigger qemu_irq_pulse!\n");
        }
    }
    if (s->stat0_read) {
        s->reg[GD32F4_I2C_REG_STAT0] &= ~0x10; // clear STPDET
        s->stat0_read = false;
    }
}

static void gd32f4_i2c_write_reg_data(gd32f4_i2c_state *s)
{
    if (s->reg[GD32F4_I2C_REG_STAT0] & 0x1) {   // SBSEND set
        const uint8_t addr = s->reg[GD32F4_I2C_REG_DATA];
        if ((addr & 0xf8) == 0xf0) {
            printf("%s: qemu do not support 10-bit addr now!\n", TYPE_GD32F4_I2C);
        }
        s->reg[GD32F4_I2C_REG_STAT0] &= ~0x1;   // SBSEND clear
        i2c_start_transfer(s->bus, addr >> 1, (addr & 1) ? true : false);
    
        s->reg[GD32F4_I2C_REG_STAT0] |= 0x2;    // ADDSEND set
        if (addr & 1) {
            s->reg[GD32F4_I2C_REG_STAT0] |= 0x40;   // RBNE set
            s->rx_temp = i2c_recv(s->bus) & 0xff;
        } else {
            s->reg[GD32F4_I2C_REG_STAT0] |= 0x80;   // TBE set
            s->reg[GD32F4_I2C_REG_STAT1] |= 0x4;    // TR set
        }
        s->reg[GD32F4_I2C_REG_STAT0] |= 0x4;    // BTC set
        if (s->reg[GD32F4_I2C_REG_CTL1] & 0x600) {  // EVIE or BUFIE set
            qemu_irq_pulse(s->irq[0]);
            qemu_printf("GD32F4_I2C_REG_DATA BTC write trigger qemu_irq_pulse!\n");
        }
    } else {
        if (s->reg[GD32F4_I2C_REG_STAT0] & 0x80) {  // TBE set
            i2c_send(s->bus, (uint8_t)s->reg[GD32F4_I2C_REG_DATA]);
            s->reg[GD32F4_I2C_REG_STAT0] |= 0x4;    // BTC set
            if ((s->reg[GD32F4_I2C_REG_CTL1] & 0x600) && (s->reg[GD32F4_I2C_REG_CTL1] & 0x04)) {  // EVIE or BUFIE set
                qemu_irq_pulse(s->irq[0]);
                qemu_printf("GD32F4_I2C_REG_DATA EVIE write trigger qemu_irq_pulse!\n");
            }
        }
    }
}

static void gd32f4_i2c_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    gd32f4_i2c_state *s = (gd32f4_i2c_state *)opaque;
    const uint32_t reg_index = (uint32_t)(offset >> 2);
    const uint32_t old_val = s->reg[reg_index];

    if (size == sizeof(uint32_t)) {
        s->reg[reg_index] = (uint32_t)val;
    } else {
        *(uint16_t *)((uint8_t *)s->reg + offset) = (uint16_t)val;
    }

    switch (reg_index) {
    case GD32F4_I2C_REG_CTL0:
        gd32f4_i2c_write_reg_ctl0(s);
        break;
    case GD32F4_I2C_REG_DATA:
        gd32f4_i2c_write_reg_data(s);
        break;
    case GD32F4_I2C_REG_STAT0:
        if (!(offset & GD32F4_I2C_SHIFT_3) && size > 1) {
            s->reg[GD32F4_I2C_REG_STAT0] = (old_val & 0xff) | (old_val & ((uint32_t)val & 0xdf00));
        }
        break;
    case GD32F4_I2C_REG_STAT1:
        s->reg[GD32F4_I2C_REG_STAT1] = old_val;
        break;
    case GD32F4_I2C_REG_SAMCS:
        if (!(offset & GD32F4_I2C_SHIFT_3)) {
            s->reg[GD32F4_I2C_REG_SAMCS] = (old_val & 0x300) | (old_val & ((uint32_t)val & 0xf000)) \
                | ((uint32_t)val & 0xf3);
        }
        break;
    default:
        break;
    }
}

static const MemoryRegionOps gd32f4_i2c_ops = {
    .read = gd32f4_i2c_read,
    .write = gd32f4_i2c_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid.min_access_size = 2,
    .valid.max_access_size = 4,
};

static void gd32f4_i2c_init(Object *obj)
{
    DeviceState *dev = DEVICE(obj);
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
    gd32f4_i2c_state *s = GD32F4_I2C(obj);

    memory_region_init_io(&s->iomem, obj, &gd32f4_i2c_ops, s, TYPE_GD32F4_I2C, 0x400);
    sysbus_init_mmio(sd, &s->iomem);

    s->bus = i2c_init_bus(dev, "i2c");

    sysbus_init_irq(sd, &s->irq[0]);
    sysbus_init_irq(sd, &s->irq[1]);
}

enum i2c_eventV6 {
    I2C_START_RECV_V6,
    I2C_START_SEND_V6,
    I2C_FINISH_V6,
    I2C_NACK_V6 /* Masker NACKed a receive byte.  */
};

// mcu send
static void gd32f4_i2c_tx_finish(gd32f4_i2c_state *state)
{
    // stop clear TBE
    state->reg[GD32F4_I2C_REG_STAT0] &= ~0x80;
    // I2C_STAT0 bit2 BTC 0000 0100=0x04
    state->reg[GD32F4_I2C_REG_STAT0] &= ~0x04;
    // I2C_CTL1 bit9 EVIE 10 0000 0000
    if (state->reg[GD32F4_I2C_REG_CTL1] & 0x200) {
        qemu_irq_pulse(state->irq[0]);
    }
    printf("mcu tx finish\n");
}

// mcu receive
static void gd32f4_i2c_rx_finish(gd32f4_i2c_state *state)
{
    // stop I2C_STAT0 update STPDET 0001 0000=0x10
    state->reg[GD32F4_I2C_REG_STAT0] |= 0x10;
    // I2C_CTL1 bit9 EVIE 10 0000 0000
    if (state->reg[GD32F4_I2C_REG_CTL1] & 0x200) {
        qemu_irq_pulse(state->irq[0]);
    }
    printf("mcu rx finish \n");
}

static int gd32f4_i2c_event(gd32f4_i2c_state *s, enum i2c_eventV6 event)
{
    switch (event) {
        case I2C_START_SEND_V6:
            printf("start send\n");
            s->transfer_start = true;
            s->transfer_recv = true;
            break;
        case I2C_START_RECV_V6:
            printf("start recv\n");
            s->transfer_start = true;
            s->transfer_recv = false;
            break;
        case I2C_FINISH_V6:
            // mcu receive
            if (s->transfer_recv) {
                gd32f4_i2c_rx_finish(s);
            } else {
                gd32f4_i2c_tx_finish(s);
            }
            s->transfer_start = false;
            break;
        case I2C_NACK_V6:
            printf("nack\n");
            break;
        default:
            break;
    }

    return 0;
}

// imcu read
static uint8_t gd32f4_i2c_rx(gd32f4_i2c_state *state, uint8_t data)
{
    if (state->transfer_start && state->transfer_recv) {
        state->rx_temp = data;
        // read data i
        state->reg[GD32F4_I2C_REG_DATA] = data;
        // set I2C_STAT0 RBNE:bit6 0100 0000=0x40
        state->reg[GD32F4_I2C_REG_STAT0] |= 0x40;
        // I2C_CTL1 bit9 EVIE 10 0000 0000
        if (state->reg[GD32F4_I2C_REG_CTL1] & 0x200) {
            qemu_irq_pulse(state->irq[0]);
        }
        printf("mcu read 0x%02x\n", data);
    }

    return 0;
}
// imcu write
static int gd32f4_i2c_tx(gd32f4_i2c_state *state)
{
    if (state->transfer_start && !state->transfer_recv) {
        state->reg[GD32F4_I2C_REG_STAT0] |= 0x80;
        if (state->reg[GD32F4_I2C_REG_CTL1] & 0x200) {
            qemu_irq_pulse(state->irq[0]);
        }
        printf("mcu write 0x%02x\n", state->reg[GD32F4_I2C_REG_DATA] & 0xFF);
        return state->reg[GD32F4_I2C_REG_DATA] & 0xFF;
    }
    return 0;
}

static void gd32f4_i2c_set_addr(gd32f4_i2c_state *state, uint8_t data)
{
    uint8_t address = state->reg[GD32F4_I2C_REG_SADDR0] & 0xFE;
    // I2C_STAT0 bit1 ADDSEND 0000 0010=0x02
    state->reg[GD32F4_I2C_REG_STAT0] |= 0x02;
    if (data == (address + 1)) {
        // addr lsb =1 set tr=1, STAT1-TR:bit3
        state->reg[GD32F4_I2C_REG_STAT1] |= 0x04;
    }
    // I2C_CTL1 bit9 EVIE 10 0000 0000
    if (state->reg[GD32F4_I2C_REG_CTL1] & 0x200) {
        qemu_irq_pulse(state->irq[0]);
    }
    printf("set addr 0x%02x\n", data);
}

static int gd32f4_i2c_backend_can_read(void *opaque)
{
    return GD32F4_I2C_READ_FRAME_LEN;
}

static void gd32f4_i2c_backend_read(void *opaque, const uint8_t *buf, int size)
{
    gd32f4_i2c_state *s = (gd32f4_i2c_state *)opaque;
    int len = 0;
    uint8_t cmd[2] = { 0 };
    for (int i = 0; i < size; i++) {
        cmd[len++] = buf[i];
        if (len < sizeof(cmd)) {
            continue;
        }
        switch (cmd[0]) {
            // I2C事件(enum i2c_event)通知外挂设备
            case 0xAA:
                gd32f4_i2c_event(s, cmd[1]);
                break;
            // I2C写数据到外挂设备
            case 0x55:
                gd32f4_i2c_rx(s, cmd[1]);
                break;
            // I2C读外挂设备数据
            case 0xA5:
                cmd[0] = 0x5A;
                cmd[1] = (uint8_t)gd32f4_i2c_tx(s);
                qemu_chr_fe_write_all(&s->chrbe, cmd, sizeof(cmd));
                break;
            // 设置外挂设备的I2C设备地址(7bits)
            case 0xF0:
                gd32f4_i2c_set_addr(s, cmd[1]);
                break;
            default:
                break;
        }
        len = 0;
    }
}

static void gd32f4_i2c_backend_event_handle(void *opaque, QEMUChrEvent event)
{
    if (event == CHR_EVENT_OPENED) {
        printf("connect...\n");
    }
}

static void gd32f4_i2c_realize(DeviceState *d, Error **errp)
{
    gd32f4_i2c_state *s = GD32F4_I2C(d);
    if (qemu_chr_fe_backend_connected(&s->chrbe)) {
        qemu_chr_fe_set_handlers(&s->chrbe,
                                 gd32f4_i2c_backend_can_read,
                                 gd32f4_i2c_backend_read,
                                 gd32f4_i2c_backend_event_handle,
                                 NULL,
                                 s,
                                 NULL,
                                 true);
    }
}

static void gd32f4_i2c_finalize(Object *obj)
{
}

static void gd32f4_prop_set_i2c_data_tx(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    gd32f4_i2c_state *state = (gd32f4_i2c_state *)obj;
    uint16_t *ptr = (uint16_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint16(v, name, ptr, errp);
    uint8_t address = state->reg[GD32F4_I2C_REG_SADDR0] & 0xFE;
    uint8_t header = (uint8_t)(state->tx_temp >> 8);
    uint8_t data = (uint8_t)(state->tx_temp & 0xFF);
    switch (header) {
        case 0x00:
            /* msb-bit5 writable 0020 xxxx xxxx xxxx = 0x2000,msb-bit4 readable 0001 xxxx xxxx xxxx
               check writable bit by "& 0x2000" */
            state->tx_temp = state->reg[GD32F4_I2C_REG_DATA] & 0xFF;
            state->reg[GD32F4_I2C_REG_STAT0] |= 0x80;
            break;
        case 0x01:
            // I2C_STAT0 bit1 ADDSEND 0000 0010=0x02
            state->reg[GD32F4_I2C_REG_STAT0] |= 0x02;
            // I2C_STAT0 bit2 BTC 0000 0100=0x04
            state->reg[GD32F4_I2C_REG_STAT0] &= ~0x04;
            if (data == (address + 1)) {
                // addr lsb =1 set tr=1, STAT1-TR:bit3
                state->reg[GD32F4_I2C_REG_STAT1] |= 0x04;
            }
            break;
        case 0x02:
            // stop clear TBE
            state->reg[GD32F4_I2C_REG_STAT0] &= ~0x80;
            // I2C_STAT0 bit2 BTC 0000 0100=0x04
            state->reg[GD32F4_I2C_REG_STAT0] &= ~0x04;
            break;
        default:
            break;
    }
    // I2C_CTL1 bit9 EVIE 10 0000 0000 when SBSEND,ADDSEND,STPDET,BTC,BUFIE,TBE,RBNE=1 trigger irq
    if (state->reg[GD32F4_I2C_REG_CTL1] & 0x200) {
        qemu_irq_pulse(state->irq[0]);
        qemu_printf("gd32f4_prop_set_i2c_data_tx trigger qemu_irq_pulse!\n");
    }
}

static void gd32f4_prop_get_i2c_data_tx(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    uint16_t *ptr = (uint16_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint16(v, name, ptr, errp);
}

static const PropertyInfo gd32f4_prop_i2c_data_tx = {
    .name  = "uint16",
    .get   = gd32f4_prop_get_i2c_data_tx,
    .set   = gd32f4_prop_set_i2c_data_tx,
    // .realized_set_allowed = true,
};

static void gd32f4_prop_set_i2c_data_rx(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    gd32f4_i2c_state *state = (gd32f4_i2c_state *)obj;
    uint16_t *ptr = (uint16_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint16(v, name, ptr, errp);
    uint8_t address = state->reg[GD32F4_I2C_REG_SADDR0] & 0xFE;
    uint8_t header = (uint8_t)(state->rx_temp >> 8);
    uint8_t data = (uint8_t)(state->rx_temp & 0xFF);
    switch (header) {
        case 0x00:
            // read data i
            state->reg[GD32F4_I2C_REG_DATA] = data;
            // set I2C_STAT0 RBNE:bit6 0100 0000=0x40
            state->reg[GD32F4_I2C_REG_STAT0] |= 0x40;
            break;
        case 0x01:
            // I2C_STAT0 bit1 ADDSEND 0000 0010=0x02
            state->reg[GD32F4_I2C_REG_STAT0] |= 0x02;
            if (data == (address + 1)) {
                // addr lsb =1 set tr=1, STAT1-TR:bit3
                state->reg[GD32F4_I2C_REG_STAT1] |= 0x04;
            }
            break;
        case 0x02:
            // stop I2C_STAT0 update STPDET 0001 0000=0x10
            state->reg[GD32F4_I2C_REG_STAT0] |= 0x10;
            break;
        default:
            break;
    }
    // I2C_CTL1 bit9 EVIE 10 0000 0000
    if (state->reg[GD32F4_I2C_REG_CTL1] & 0x200) {
        qemu_irq_pulse(state->irq[0]);
    }
}
 
static void gd32f4_prop_get_i2c_data_rx(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    uint16_t *ptr = (uint16_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint16(v, name, ptr, errp);
}

static const PropertyInfo gd32f4_prop_i2c_data_rx = {
    .name  = "uint16",
    .get   = gd32f4_prop_get_i2c_data_rx,
    .set   = gd32f4_prop_set_i2c_data_rx,
    // .realized_set_allowed = true, // 允许在实现后修改
};

static Property gd32f4_i2c_properties[] = {
    DEFINE_PROP_CHR("chardev", gd32f4_i2c_state, chrbe),
    DEFINE_PROP_UNSIGNED_NODEFAULT("i2c-data-rx", gd32f4_i2c_state, rx_temp, gd32f4_prop_i2c_data_rx, uint16_t),
    DEFINE_PROP_UNSIGNED_NODEFAULT("i2c-data-tx", gd32f4_i2c_state, tx_temp, gd32f4_prop_i2c_data_tx, uint16_t),
    DEFINE_PROP_END_OF_LIST(),
};

static void gd32f4_i2c_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->realize = gd32f4_i2c_realize;
    dc->reset = gd32f4_i2c_reset;
    dc->vmsd = &vmstate_gd32f4_i2c;
    device_class_set_props(dc, gd32f4_i2c_properties);
}

static const TypeInfo gd32f4_i2c_info = {
    .name          = TYPE_GD32F4_I2C,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(gd32f4_i2c_state),
    .instance_init = gd32f4_i2c_init,
    .instance_finalize = gd32f4_i2c_finalize,
    .class_init    = gd32f4_i2c_class_init,
};

static void gd32f4_i2c_register_types(void)
{
    type_register_static(&gd32f4_i2c_info);
}

type_init(gd32f4_i2c_register_types)
